24.7. Type-safe Configuration Properties
24.7. 类型安全的配置属性
使用@Value("${property}")
注解注入配置属性有时会比较难处理,特别是但你需要使用多个properties,或者数据本身有层次结构的时候。Spring Boot提供一种可选的使用配置的方法,这种方法让强类型的beans来管理和校验应用的配置,如下所示:
package com.example;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("acme")
public class AcmeProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
public boolean isEnabled() { ... }
public void setEnabled(boolean enabled) { ... }
public InetAddress getRemoteAddress() { ... }
public void setRemoteAddress(InetAddress remoteAddress) { ... }
public Security getSecurity() { ... }
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
public String getUsername() { ... }
public void setUsername(String username) { ... }
public String getPassword() { ... }
public void setPassword(String password) { ... }
public List<String> getRoles() { ... }
public void setRoles(List<String> roles) { ... }
}
}
之前的POJO定义了如下的属性:
acme.enabled,默认值为false
acme.remote-address,带有一个能从String转换的类型
acme.security.username,有一个嵌套的“security”对象,它的名字由属性名定义。特别的,返回类型没有在那儿被使用,本来可以是SecurityProperties。
acme.security.password
acme.security.roles,一个String的集合
注意 getters和setters时常是必须的。因为跟Spring MVC一样,绑定是通过标准的Java Beans属性描述符进行的。但是也有不需要setter的情况:
Maps,只要它们被初始化了,需要getter,但setter不是必须的。因为binder能够改变它们。
Collections和arrays能够通过索引(通常用YAML),或者使用单个的由逗号分隔的值(属性)存取。 在后面的情况下,setter是必须的。对于这些类型,我们建议总是加上setter。如果你初始化一个collection,确保它不是不可变的(如同之前的例子)。
如果嵌套的POJO属性被初始化(就像之前例子里的Security域),setter就不需要。如果你想要binder使用它默认的构造器,即时创建一个实例,你就需要一个setter。
有些人使用Project Lombok自动添加getters和setters。确保Lombok不会为这些类型生成任何特别的构造器,因为它会自动地被容器使用,来实例化对象。
提示 查看@Value和@ConfigurationProperties之间的区别。
你需要在@EnableConfigurationProperties
注解中列出要注册的属性类,如下所示:
@Configuration
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
注 当@ConfigurationProperties
bean以这种方式注册时,该bean将有个约定的名称:<prefix>-<fqn>
,<prefix>
是@ConfigurationProperties
注解中定义的environment key前缀,<fqn>
是bean的全限定名。如果注解中没有提供任何前缀,那就只使用bean的全限定名。上述示例中的bean名称将是acme-com.example.AcmeProperties
。
尽管上述配置为FooProperties
创建了一个常规的bean,不过我们建议@ConfigurationProperties
只用来处理environment(只用于注入配置,系统环境之类的),特别是不要注入上下文中的其他beans。话虽如此,@EnableConfigurationProperties
注解会自动应用到你的项目,任何存在的,注解@ConfigurationProperties
的bean将会从Environment
中得到配置。只要确定AcmeProperties
是一个已存在的bean,MyConfiguration
就可以不用了。
@Component
@ConfigurationProperties(prefix="acme")
public class AcmeProperties {
// ... see the preceding example
}
这种配置风格跟SpringApplication
的外部化YAML配置配合的很好:
# application.yml
acme:
remote-address: 192.168.1.1
security:
username: admin
roles:
- USER
- ADMIN
# additional configuration as required
为了使用@ConfigurationProperties
beans,你可以像使用其他bean那样注入它们:
@Service
public class MyService {
private final AcmeProperties properties;
@Autowired
public MyService(AcmeProperties properties) {
this.properties = properties;
}
//...
@PostConstruct
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
// ...
}
}
注 使用@ConfigurationProperties
能够产生可被IDEs使用的元数据文件,来为你自己的键值提供自动补全,具体参考[Appendix B, Configuration meta-data](../X. Appendices/B. Configuration meta-data.md)。